package com.jd.platform.sample4.util;

//import com.jd.platform.sample3.entity.pojo.Produce;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.List;

//TODO: 每个类的属性都有一个id，redis不用存（主要是AttributeUtil再写多一个方法，从数据库获取后id赋值null）
//@Service
@Component
public interface RedisService<T> extends RedisKey<T> {
    @Autowired
    Cache cache=new Cache();

//    String pojo="Produce";
//
//    long sleepTime=500; //睡眠时间=更新数据库所需时间=500ms=0.5s

    String getPojo();

    //i的值：  1是InsertOrUpdate，2是InsertOrUpdates
    //        3是RemoveById。4是RemoveByIds
    long getSleepTime(int i);

    T GetById(long id);

    void SaveOrUpdate(T object);

    void SaveOrUpdateBatch(List<T> objects);

    void RemoveById(long id);

    void RemoveByIds(List<Long> ids);


    /**
     * 以下是查询，查询 一个属性值 或 一个对象所有属性的值
     */
    // 根据id查Produce某一属性的值
    default String getValueById(long id,String attribute) throws Exception {
//        if (attribute==null) { /* 这里测试默认attribute="name" */
//            attribute="name";
//        }
        String key=GetKeyById(attribute,id);
        String value=cache.get(key);
        if(value==null) {
            T produce = this.GetById(id); //尝试从数据库获取值
            if(produce!=null){ //redisTemplate没有，数据库有
                value = (String) AttributeUtil.getAttributeValue(produce,attribute);
            } else { //该key不存在即没有存值时，视为被恶意访问了多次，为避免缓存穿透，设置值为""
                value=""; //TODO: 记得写过期时间，因为是恶意访问不需要存太久；这里Cache没写对应的方法
            }
            cache.set(key,value);
        }
        System.out.println(value);
        return value;
    }

    // 根据id查Produce所有属性的值
    default T getObjectById(long id) throws Exception {
        T produce = null;

//        //第一种方法
//        List<String> attributes = AttributeUtil.getAttributeNames(Produce.class); //属性名集合
//        for(int i=0;i<attributes.size();i++) {
//            String attribute = attributes.get(i);
//            String value = getValueById(id, attribute); //一次一次的从redis获取大大消耗redis连接
//            AttributeUtil.setAttributeValue(produce, attribute, value);
//        }

        //第二种方法（推荐）
        //先遍历JdHotKeyStore.getValue(key)
        //查不到的数据放一起用redisTemplate.opsForValue().multiGet(keys)
        //还有数据查不到直接统一用this.getById(id)
        //还查不到的话就判定为恶意访问，赋默认值并批量缓存cache.sets(keys,"")
        List<String> keys = GetKeysById(id); //key集合
        String[] values=cache.gets(keys); //来自于redis的values
        if(values!=null) {
            for(int i=0;i< keys.size();i++) {
                AttributeUtil.setAttributeValue(produce, keys.get(i), values[i]);
            }
        } else {
            produce = this.GetById(id); //尝试从数据库获取值
            if(produce!=null) {
                List<String> values2 = AttributeUtil.getAttributeValues(produce, keys); //属性值集合
//                List<String> values3 = getKeys(values2);
                cache.sets(keys,values2);
            } else {
                cache.sets(keys,"");
            }
        }


        System.out.println(produce);
        return produce;
    }

    /**
     * TODO: 条件查询
     * View → Tool Windows → 该字符串，会显示项目中所有包含 该字符串 的注释，用于标记未完善的地方方便后续寻找并完善
     *
     * 条件查询真封装到redis太复杂了，mysql还有索引帮助查询，所以没必要，这里只介绍相对容易实现的简单条件查询
     * 一、根据已知值num获取price大于（或小于等于）num的Produce
     * 二、根据已知属性值查Produce剩余未知属性的值（可能是0-n个Produce）
     */


    /**
     * 以下是更新数据库（添加、修改、删除），redis需与数据库保持一致，以下是解决方案
     * 一、延迟双删    （实现简单，把握好延迟时间即可）（本项目使用该方案）
     * 二、监听binlog删除+mq队列异步重试
     */
    //单独添加或更新
    default void InsertOrUpdate(T produce) throws Exception {
        List<String> keys = GetKeysById(produce); //key集合
        //属性值集合（这里不需要，因为get如果redis获取不到则从数据库获取并存储到redis中）
//        List<String> values = AttributeUtil.getAttributeValues(produce, keys);

        cache.removes(keys);
        this.SaveOrUpdate(produce);
        Thread.sleep(getSleepTime(1));
        cache.removes(keys);
    }

    //批量添加或更新
    default void InsertsOrUpdates(List<T> produces) throws Exception {
        List<String> keys = GetKeysById(produces); //key集合 TODO:不确定会不会出问题
        //属性值集合（这里不需要，因为get如果redis获取不到则从数据库获取并存储到redis中）
//        List<String> values = AttributeUtil.getAttributeValues(produce, keys);

        cache.removes(keys);
        this.SaveOrUpdateBatch(produces);
        Thread.sleep(getSleepTime(2));
        cache.removes(keys);
    }

    //单独删除
    default void delete(long id) throws Exception {
        List<String> keys = GetKeysById(id); //key集合
        //属性值集合（这里不需要，因为get如果redis获取不到则从数据库获取并存储到redis中）
//        List<String> values = AttributeUtil.getAttributeValues(produce, keys);

        cache.removes(keys);
        this.RemoveById(id);
        Thread.sleep(getSleepTime(3));
        cache.removes(keys);
    }

    //批量删除
    default void deletes(List<Long> ids) throws Exception {
        List<String> keys = GetKeysById(ids); //key集合
        //属性值集合（这里不需要，因为get如果redis获取不到则从数据库获取并存储到redis中）
//        List<String> values = AttributeUtil.getAttributeValues(produce, keys);

        cache.removes(keys);
        this.RemoveByIds(ids);
        Thread.sleep(getSleepTime(4));
        cache.removes(keys);
    }



    /**
     * 以下方法是以上方法的组合
     */
    default Object getById_Redis(long id,String... attributes) throws Exception {
        if (attributes == null || attributes.length == 0) {
            return getObjectById(id); //返回Produce
        } else {
            return getValueById(id,attributes[0]); //返回属性名为attributes[0]的属性值
        }
    }

    default void InsertOrUpdate_Redis(Object object) throws Exception {
        if(!(object instanceof List)){
//            Produce produce = (Produce) object;
            InsertOrUpdate((T) object);
        } else {
            List<T> produces = (List<T>) object;
            InsertsOrUpdates(produces);
        }
    }

    default void deleteById_Redis(Object object) throws Exception {
        if(!(object instanceof List)){
            long id = (Long) object;
            delete(id);
        } else {
            List<Long> ids = (List<Long>) object;
            deletes(ids);
        }
    }
}
